﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Graph;

namespace Crawler
{
    public class WebCrawler
    {
        private WebSiteGraph _graph;
        private Queue<IHtmlDoc> _queue;
        private int _currentLevel = 0;
        private IHtmlDoc _rootDoc = null;
        private Uri _rootUri = null;

        public WebCrawler()
        {
            _graph = new WebSiteGraph(false);
            _queue = new Queue<IHtmlDoc>();
        }

        public ICollection<IHtmlDoc> GetAdjecent(IHtmlDoc doc)
        {
            return _graph.GetAdjacents(doc);
        }

        public IHtmlDoc RootDoc
        {
            get { return _rootDoc; }
        }

        public void BuildGraphOLD(string url, int maxLevel)
        {
            _rootDoc = new HtmlDoc(url);

            _queue.Enqueue(_rootDoc);
            _graph.Add(_rootDoc);

            IHtmlDoc currentDoc;

            int i = 1;
            int j = 0;
            int t = 0;
            while (_queue.Count > 0)
            {
                currentDoc = _queue.Dequeue();
                List<IHtmlDoc> docs = currentDoc.GetAllLinks();
                if (docs != null)
                {
                    foreach (IHtmlDoc d in docs)
                    {
                        bool c = _graph.Contains(d);
                        if (!c)
                        {
                            if (maxLevel == -1 || _currentLevel < maxLevel)
                            {
                                Console.WriteLine(_queue.Count + " - " + _graph.CountVertices() + ": " + d.URL);
                                _queue.Enqueue(d);
                            }
                            _graph.Add(d);
                            _graph.AddEdge(currentDoc, d);

                        }
                        else
                        {
                            if (maxLevel == -1 || _currentLevel < maxLevel)
                            {
                                IHtmlDoc existing = _graph.Find(d);
                                if (existing != null)
                                    _graph.AddEdge(currentDoc, existing);
                            }
                        }
                        j++;
                    }
                }

                if (--i == 0)
                {
                    _currentLevel++;
                    i = j;
                    j = 0;
                }
            }
        }

        public void BuildGraph(string url)
        {
            BuildGraph(url, -1);
        }

        public void BuildGraph(string url, int maxLevel)
        {
            _rootDoc = new HtmlDoc(url);
            _rootUri = new Uri(url);
            _queue.Enqueue(_rootDoc);

            IHtmlDoc doc = null;

            int cLevel = 1; // Keep track of elements on current level, still to scan.
            int cCount = 0; // Keep track of the number of new elements in the next level.

            while (_queue.Count > 0)
            {
                doc = _queue.Dequeue();

                if (!_graph.Contains(doc))
                {
                    _graph.Add(doc);
                    if (doc.ParentDoc != null)
                        _graph.AddEdge(doc.ParentDoc, doc);

                    Console.WriteLine("Adding: " + doc);
                    Uri u = new Uri(doc.URL);
                    if (u.Host == _rootUri.Host)
                    {
                        if (!doc.URL.ToLower().EndsWith(".jpg") & !doc.URL.ToLower().EndsWith(".gif") & !doc.URL.ToLower().EndsWith(".png"))
                        {
                            List<IHtmlDoc> links = doc.GetAllLinks();
                            foreach (IHtmlDoc d in links)
                            {
                                d.ParentDoc = doc;
                                if (!_queue.Contains(d) && !_graph.Contains(d))
                                {
                                    Console.WriteLine("\t- Found New Link: " + d);
                                    if (maxLevel == -1 || _currentLevel <= maxLevel)
                                        _queue.Enqueue(d);
                                }
                                cCount++;
                            }
                            Console.WriteLine();
                        }
                    }
                }

                if (--cLevel == 0)
                {
                    _currentLevel++;
                    cLevel = cCount;
                    cCount = 0;
                }
            }
        }

        public override string ToString()
        {
            return OutputGraph();
        }

        public string OutputGraph()
        {
            if (_rootDoc != null)
                return DepthFirstTraversal(_rootDoc);
            return "Graph have no root document";
        }

        private string DepthFirstTraversal(IHtmlDoc start, int startLevel)
        {
            string str = "";
            string t = "  ";
            string tab = t;

            for (int i = 0; i < startLevel; i++)
                tab += t;

            start.Visited = true;
            ICollection<IHtmlDoc> adjecent = _graph.GetAdjacents(start);
            foreach (IHtmlDoc d in adjecent)
            {
                if (d.Visited)
                    str += "\n" + tab + d.URL;
                else
                    str += "\n" + tab + DepthFirstTraversal(d, startLevel + 1);

            }

            return start.URL + str;
        }

        private string DepthFirstTraversal(IHtmlDoc start)
        {
            return DepthFirstTraversal(start, 0);
        }
    }
}
